Install

On linux:

$ sudo apt install git

On Mac/Windows probably better to use conda / anaconda. Mac system version is outdated.

Configuration

At the very minimum:

$ git config --global user.name "Vasya Pupkin"
$ git config --global user.email "vasya.pupkin@russianhackers.org"

probably makes sense to set the default editor for commit messages:

$ git config --global core.editor vim

Initialize repository

Initialize:

$ git init
$ ls -lah

Note .git directory. That's where all the magic lives.

Add stuff to the repo.

$ git add .

Can be more specific here (git add *.py).

Let's look at the state of the repository.

$ git status

And now commit the changes.

$ git commit -a -m "Initial commit"

File states as tracked by git

Every file in a repository can be in one of three states: commited, staged, modified.

Working dir Staging Repository <--------------------- checkout ----------------------| |------ stage (add) -------> |--------- commit --------->|

Cloning

All the above repo creation steps can be done on github.com. When creating a new repo make sure to include LICENSE otherwise the default copyright will apply which means "all rights reserved".

Then the remote repo (on any server) can be copied via clone command:

$ git clone https://github.com/eco32i/biodata.git

This will create a new directory biodata, set up a new git repo in it (.git directory), point to the remote address and retrieve the latest version of the code from the remote.

Workflow

What happens to a file in the repo:

  • when you edit a (tracked) file, git flags it "modified"
  • this "stages" file for commit
  • when you commit, all staged changes are recorded in the repo and "modified" files are unflagged

Use status command to check the state of your repo:

$ git status

Aside: HEAD always points to the latest commit (unless it's detached).

Push your changes to the remote:

$ git push origin master

Remotes

Repo can have as many remotes as one wishes:

$ git remote -v

To add a remote:

$ git remote add upstream https://github.com/vasyapupkin/project

Now to fetch the content of a remote, use fetch command:

$ git fetch [remote-name]

Note that this will not change your working directory (won't do merge).

To push your changes to a remote:

$ git push [remote-name] [branch-name]

Undoing and reverting

If you want to checkout or pull a remote and have uncommitted changes:

$ git stash

This will temporarily hide your changes. To restpore them:

$ git stash apply

Fix commit mistakes

Say, you forgot to include a new file into your last commit:

$ git commit --amend

Unstaging file:


$ git reset HEAD index.ipynb
Unstaged changes after reset:
M index.html

Undo a commit:

$ git revert HEAD

This will only work for the last commit.

To switch to a different commit:

First identify the commit you want to revert to

$ git log

To temporarily switch to a commit (pro-tip: use Tab-completion)

$ git checkout [commit-hash]

Same but checkout old commit as a branch

$ git checkout -b old-state [commit-hash]

To toss everything since the old commit

$ git reset --hard [commit-hash]

If you want to keep your work

$ git stash
$ git reset --hard [commit-hash]
$ git stash pop

Undo published commits: you probably don't want to reset the branch, since that's effectively rewriting history. In that case, you could revert the commits. With Git, revert has a very specific meaning: create a commit with the reverse patch to cancel it out. This way you don't rewrite any history.

# This will create three separate revert commits:
$ git revert a867b4af 25eee4ca 0766c053

# It also takes ranges. This will revert the last two commits:
$ git revert HEAD~2..HEAD

# Then commit. Be sure and write a good message describing what you just did
$ git commit

Branching

Branches are cheap and easy. Always do branches! They are also integral part of github workflow.

To create and checkout a new branch:

$ git checkout -b test

To list all branches:

$ git branch

Generally all development should happen in branches, master branch should be for the stable production code.

Github workflow

  • (fork the project)
  • create a branch
  • add commits
  • open a pull request
  • discuss
  • deploy (check your branch code in production)
  • merge

Merging

Fast-forward merging

The HEAD moves to the most recent commit in the branch being merged in. Only works if there is no divergent work in that branch.

$ git checkout master
$ git merge test

You can explicitly disable Fast-forward. In that case a new commit object will be created.

$ git merge test --no-ff

Three-way Merge

If the branches have diverged the three-way merge is done: a new snapshot created from the most recent commits in each branch and a new commit that points to it.

Merge conflicts

If automatic three-way merge fails, the conflicts must be resolved manually. Use status command to identify files that need fixing:

$ git status
# On branch master
# Unmerged paths:
# (use “git add/rm …” as appropriate to mark resolution)
#
# both modified: index.html
#

$ git diff master test

Then edit those files (they will have conflict markers in places that failed to merge):

My name is
 <<<<<<< HEAD
 Jane
 =======
 Mary
 >>>>>>> branch-test

Edit each of the conflicting files, then use git add <filename> to mark them as resolved

Rebasing

Use case: you created a topic branch and started working on it. In the meantime others updated master. You want to stay on your topic branch and keep working on it but you want it to incorporate latest changes from master.

$ git checkout test
... do some work ...
$ git fetch master
$ git rebase master

This rewrites the history by replaying all commits from one branch (master) on another (test).


In [ ]: